package org.chaiyi.template.core.common.util;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * @author Gong.Yang
 */
public class ClassPathUtil {
    public interface ClassMatch {
        boolean match(Class<?> clazz);
    }

    /**
     * 扫描包路径下所有的类，
     * 性能比spring的ClassPathScanningCandidateComponentProvider要好一点
     * @param packageName 包路径
     * @param classMatch 匹配条件
     * @return 符合条件的类
     */
    public static List<Class<?>> scanPackage(String packageName, ClassMatch classMatch) {
        List<Class<?>> targetClasses = new ArrayList<>();

        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            String path = packageName.replace('.', '/');
            Enumeration<URL> resources = classLoader.getResources(path);

            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                String decodedPath = URLDecoder.decode(resource.getFile(), "UTF-8");//中文路径问题
                File directory = new File(decodedPath);

                if (directory.exists()) {
                    File[] files = directory.listFiles();

                    if (files != null) {
                        for (File file : files) {
                            if (file.isDirectory()) {
                                // Recursive call for subdirectories
                                List<Class<?>> subClasses = scanPackage(
                                        packageName + "." + file.getName(), classMatch);
                                targetClasses.addAll(subClasses);
                            } else if (file.getName().endsWith(".class")) {
                                // Remove the ".class" extension
                                String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);

                                try {
                                    Class<?> clazz = Class.forName(className);
                                    // Check if the class implements the Target interface
                                    if (classMatch.match(clazz)) {
                                        targetClasses.add(clazz);
                                    }
                                } catch (ClassNotFoundException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return targetClasses;
    }
}
